Skip to content

运维脚本示例:MySQL 备份、主从监控与 Nginx 日志

请在实际环境中替换密码、路径与收件人。


一、MySQL:按库整库备份(单循环)

对每个非系统库生成一个带时间戳的 .sql 文件。

shell
#!/bin/bash
DATE=$(date +%F_%H-%M-%S)
HOST=localhost
USER=backup
PASS='请填写备份账号密码或使用环境变量 MYSQL_BACKUP_PASS'
BACKUP_DIR=/data/db_backup
DB_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "show databases;" 2>/dev/null |egrep -v "Database|information_schema|mysql|performance_schema|sys")

for DB in $DB_LIST; do
    BACKUP_NAME=$BACKUP_DIR/${DB}_${DATE}.sql
    if ! mysqldump -h$HOST -u$USER -p$PASS -B $DB > $BACKUP_NAME 2>/dev/null; then
        echo "$BACKUP_NAME 备份失败!"
    fi
done

补充-p 紧跟密码会在 ps 中暴露;生产环境常用 ~/.my.cnf 权限 600 或专用备份账号 + 最小权限。


二、MySQL:库 → 表逐级备份(嵌套循环)

每个库一个目录,每张表单独 SQL 文件,适合极大库或需要按表恢复的场景。

shell
#!/bin/bash
DATE=$(date +%F_%H-%M-%S)
HOST=localhost
USER=backup
PASS='请填写备份账号密码或使用环境变量 MYSQL_BACKUP_PASS'
BACKUP_DIR=/data/db_backup
DB_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "show databases;" 2>/dev/null |egrep -v "Database|information_schema|mysql|performance_schema|sys")

for DB in $DB_LIST; do
    BACKUP_DB_DIR=$BACKUP_DIR/${DB}_${DATE}
    [ ! -d $BACKUP_DB_DIR ] && mkdir -p $BACKUP_DB_DIR &>/dev/null
    TABLE_LIST=$(mysql -h$HOST -u$USER -p$PASS -s -e "use $DB;show tables;" 2>/dev/null)
    for TABLE in $TABLE_LIST; do
        BACKUP_NAME=$BACKUP_DB_DIR/${TABLE}.sql 
        if ! mysqldump -h$HOST -u$USER -p$PASS $DB $TABLE > $BACKUP_NAME 2>/dev/null; then
            echo "$BACKUP_NAME 备份失败!"
        fi
    done
done

三、MySQL 主从:Slave_IO_Running / Slave_SQL_Running

show slave status 解析运行状态,不为 Yes 则发邮件(需已配置 mail)。

shell
#!/bin/bash  
HOST=localhost
USER=root
PASSWD='请填写监控用只读账号密码'
IO_SQL_STATUS=$(mysql -h$HOST -u$USER -p$PASSWD -e 'show slave status\G' 2>/dev/null |awk '/Slave_.*_Running:/{print $1$2}')
for i in $IO_SQL_STATUS; do
    THREAD_STATUS_NAME=${i%:*}
    THREAD_STATUS=${i#*:}
    if [ "$THREAD_STATUS" != "Yes" ]; then
        echo "Error: MySQL Master-Slave $THREAD_STATUS_NAME status is $THREAD_STATUS!" | mail -s "Master-Slave Status" alert@example.com
    fi
done

补充:MySQL 8.0.22+ 复制线程字段曾更名(Replica_*),新版本请对照官方文档调整 awk 模式。


四、Nginx:按日归档访问日志

将指定日志移到按年月创建的目录,文件名带昨日日期,并向 master 发 USR1 促使重新打开日志文件。

shell
#!/bin/bash
LOG_DIR=/usr/local/nginx/logs
YESTERDAY_TIME=$(date -d "yesterday" +%F)
LOG_MONTH_DIR=$LOG_DIR/$(date +"%Y-%m")
LOG_FILE_LIST="default.access.log"

for LOG_FILE in $LOG_FILE_LIST; do
    [ ! -d $LOG_MONTH_DIR ] && mkdir -p $LOG_MONTH_DIR
    mv $LOG_DIR/$LOG_FILE $LOG_MONTH_DIR/${LOG_FILE}_${YESTERDAY_TIME}
done

kill -USR1 $(cat /var/run/nginx.pid)

补充date -d 为 GNU date;macOS/BSD 需改用 -v-1d 等写法。nginx.pid 路径以实际配置为准。


五、Nginx 访问日志简单分析

假定日志格式包含:$remote_addr、时间、$request$status 等(见脚本内注释)。

shell
#!/bin/bash
# 日志格式: $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"
LOG_FILE=$1
echo "统计访问最多的10个IP"
awk '{a[$1]++}END{print "UV:",length(a);for(v in a)print v,a[v]}' $LOG_FILE |sort -k2 -nr |head -10
echo "----------------------"

echo "统计时间段访问最多的IP"
awk '$4>="[01/Dec/2018:13:20:25" && $4<="[27/Nov/2018:16:20:49"{a[$1]++}END{for(v in a)print v,a[v]}' $LOG_FILE |sort -k2 -nr|head -10
echo "----------------------"

echo "统计访问最多的10个页面"
awk '{a[$7]++}END{print "PV:",length(a);for(v in a){if(a[v]>10)print v,a[v]}}' $LOG_FILE |sort -k2 -nr
echo "----------------------"

echo "统计访问页面状态码数量"
awk '{a[$7" "$9]++}END{for(v in a){if(a[v]>5)print v,a[v]}}' $LOG_FILE |sort -k3 -nr

补充:时间过滤段里的日期是 示例,请改成你日志里真实存在的时间段;$4 是否为时间字段取决于实际 log_format 字段顺序。


六、小结

  • 备份脚本建议再加:磁盘空间检查、备份压缩、保留天数清理、失败告警
  • 日志分析流量大时可换 ClickHouse / ELK;Shell + awk 适合中小规模快速统计。